(ns thi.ng.geom.polyhedra
  #?(:cljs (:require-macros [thi.ng.math.macros :as mm]))
  (:require
   [thi.ng.geom.core :as g]
   [thi.ng.geom.attribs :as attr]
   [thi.ng.geom.vector :as v :refer [vec3]]
   [thi.ng.geom.basicmesh :as bm]
   [thi.ng.geom.gmesh :as gm]
   [thi.ng.math.core :as m :refer [PHI PI SQRT2 SQRT3]]
   [thi.ng.dstruct.core :as d]
   #?(:clj [thi.ng.math.macros :as mm])))

;; ** Polyhedra primitives / Platonic Solids
;;
;; http://paulbourke.net/geometry/platonic/
;;
;; *** Polyhedron mesh constructor
;;
;; The `polyhedron-mesh` function acts as a generic HOF constructor of
;; mesh instances for the polyhedra defined below. It takes a
;; polyhedron face list generator, mesh scale and optional subdivision
;; scheme and returns either a `BasicMesh` or `GMesh` instance.
;;
;; ```
;; (require '[thi.ng.geom.core :as g])
;; (require '[thi.ng.geom.mesh.polyhedra :as poly])
;; (require '[thi.ng.geom.mesh.subdivision :as sd])
;; (require '[thi.ng.geom.mesh.io :as mio])
;; (require '[clojure.java.io :as io])
;;
;; ; create icosahedron sphere (3x subdivided)
;; (with-open [o (io/output-stream "icosphere.stl")]
;;   (->> (poly/polyhedron-mesh poly/icosahedron sd/catmull-clark 1 3)
;;        (g/tessellate)
;;        (mio/write-stl (mio/wrapped-output-stream o))))
;; ```
;;
;; Result: http://media.thi.ng/geom/mesh/icosphere.svg

(defn polyhedron-mesh
  ([f] (polyhedron-mesh f nil))
  ([f {:keys [scale subdiv iter attribs mesh]}]
   (let [faces (f (or scale 1.0) attribs)]
     (if subdiv
       (let [m (d/iterate-n (or iter 1) subdiv (g/into (gm/gmesh) faces))]
         (if mesh (g/into mesh m) m))
       (g/into (or mesh (bm/basic-mesh)) faces)))))

(defn tetrahedron-vertices
  [scale]
  (let [p (/ SQRT3 3.0)
        q (/ p -2.0)
        r (/ (Math/sqrt 6) 6.0), r' (- r)]
    (map #(g/scale % scale)
         [(vec3 p 0 r')
          (vec3 q -0.5 r')
          (vec3 q 0.5 r')
          (vec3 0 0 r)])))

(defn tetrahedron
  [scale attribs]
  (let [[a b c d] (tetrahedron-vertices scale)]
    (map-indexed
     (fn [i fverts] (attr/generate-face-attribs fverts i attribs nil))
     [[a b c] [a c d] [a d b] [c b d]])))

(defn octahedron-vertices
  [scale]
  (let [p (/ (* 2.0 SQRT2)), p' (- p)
        q 0.5,               q' (- q)]
    (map #(m/normalize % scale)
         [(vec3 p' 0 p)
          (vec3 p 0 p)
          (vec3 p 0 p')
          (vec3 p' 0 p')
          (vec3 0 q 0)
          (vec3 0 q' 0)])))

(defn octahedron
  [scale attribs]
  (let [[a b c d e f] (octahedron-vertices scale)]
    (map-indexed
     (fn [i fverts] (attr/generate-face-attribs fverts i attribs nil))
     [[d a e] [c d e] [b c e] [a b e]
      [d c f] [a d f] [c b f] [b a f]])))

(defn icosahedron-vertices
  [scale]
  (let [p 0.5,           p' (- p)
        q (/ (* 2 PHI)), q' (- q)]
    (map #(m/normalize % scale)
         [(vec3 0 q p')
          (vec3 q p 0)
          (vec3 q' p 0)
          (vec3 0 q p)
          (vec3 0 q' p)
          (vec3 p' 0 q)
          (vec3 p 0 q)
          (vec3 0 q' p')
          (vec3 p 0 q')
          (vec3 p' 0 q')
          (vec3 q p' 0)
          (vec3 q' p' 0)])))

(defn icosahedron
  [scale attribs]
  (let [[a b c d e f g h i j k l] (icosahedron-vertices scale)]
    (map-indexed
     (fn [i fverts] (attr/generate-face-attribs fverts i attribs nil))
     [[b a c] [c d b] [e d f] [g d e]
      [h a i] [j a h] [k e l] [l h k]
      [f c j] [j l f] [i b g] [g k i]
      [f d c] [b d g] [c a j] [i a b]
      [j h l] [k h i] [l e f] [g e k]])))

(defn dodecahedron-vertices
  [scale]
  (let [p 0.5,               p' (- p)
        q (/ 0.5 PHI),       q' (- q)
        r (* 0.5 (- 2 PHI)), r' (- r)]
    (map #(m/normalize % scale)
         [(vec3 r 0 p)
          (vec3 r' 0 p)
          (vec3 q' q q)
          (vec3 0 p r)
          (vec3 q q q)
          (vec3 q q' q)
          (vec3 0 p' r)
          (vec3 q' q' q)
          (vec3 r 0 p')
          (vec3 r' 0 p')
          (vec3 q' q' q')
          (vec3 0 p' r')
          (vec3 q q' q')
          (vec3 q q q')
          (vec3 0 p r')
          (vec3 q' q q')
          (vec3 p r 0)
          (vec3 p' r 0)
          (vec3 p' r' 0)
          (vec3 p r' 0)])))

(defn dodecahedron
  [scale attribs]
  (let [[a b c d e f g h i j k l m n o p q r s t] (dodecahedron-vertices scale)]
    (map-indexed
     (fn [i fverts] (attr/generate-face-attribs fverts i attribs nil))
     [[e d c b a] [h g f a b] [m l k j i] [p o n i j]
      [o d e q n] [d o p r c] [l g h s k] [g l m t f]
      [e a f t q] [m i n q t] [p j k s r] [h b c r s]])))
